#ifndef _COMM_HPP
#define _COMM_HPP

#include<iostream>
#include<string>
#include<cerrno>
#include<cstring>
#include<cstdio>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<cstdlib>
#include<cassert>

//IPC_CREAT：没有共享内存就创建，有，就返回这个共享内存的shmid
//IPC_CREAT | IPC_EXCL ：没有共享内存就创建，有，就直接报错，返回-1并设置错误码。其作用是确保创建的共享内存是最新的
//IPC_EXCL不能单独使用

using namespace std;

#define PATHNAME "." 
#define PROJID 0x6666




//获取key值
key_t getKey()
{
    key_t k=ftok(PATHNAME,PROJID);
    if(k==-1)
    {
        cerr<<"error: "<<errno<<" : "<<strerror(errno)<<endl;
        exit(1);
    }
    return k;
}

//将key值转换成十六进制
string toHex(key_t k)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%x",k);
    return buffer;
}

const int gsize=5000;


//进行封装
static int createShmHelper(key_t key,int size,int flag)
{
    int shmid = shmget(key,size,flag);

    //出错判断
    if(shmid==-1)
    {
        cerr<<"error: "<<errno<<" : "<<strerror(errno)<<endl;
        exit(2);
    }
    return shmid;
}

//创建共享内存
int createShm(key_t key,int size)
{
    //因为是创建，我们就用，保证创建最新的内存空间
    umask(0);//将掩码设置为0，保证预期的权限
    return createShmHelper(key,size,IPC_CREAT | IPC_EXCL | 0666);
}

//获取共享内存
int getShm(key_t key,int size)
{
    //只要获取就好，所以只要IPC_CREAT
    return createShmHelper(key,size,IPC_CREAT);
}

//关联共享内存
char * attachShm(int shmid)
{
    char*start=(char*)shmat(shmid,NULL,0);
    return start;
}

//取关联共享内存
void detachShm(char*start)
{
    int n=shmdt(start);
    assert(n!=-1);
    (void)n;
}

//删除共享内存
void delShm(int shmid)
{

    int n=shmctl(shmid,IPC_RMID,NULL);
    assert(n!=-1);
    (void)n;
}


#define SERVER 1
#define CLIENT 0

class Init
{
public:
    Init(int t):type(t)
    {
        //获取key值
        key_t k = getKey();
        //创建/获取共享内存
        if(type == SERVER)
        {
            //服务端创建
            shmid = createShm(k, gsize);
        }
        else
        {
            //客户端获取
            shmid = getShm(k, gsize);
        }
        //关联共享内存
        start = attachShm(shmid);
    }

    //获取读写字符串
    char *getStart()
    { 
        return start; 
    }

    ~Init()
    {
        //取关联
        detachShm(start);

        if(type == SERVER)
        {
            //如果是服务端，还需要销毁共享内存
            delShm(shmid);
        }
    }
private:
    char *start;//读写字符串
    int type; //表示是服务端还是客户端
    int shmid;//共享内存的id
};



#endif